package highLight;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.search.highlight.*;
import org.apache.lucene.search.matchhighlight.FieldValueHighlighters;
import org.apache.lucene.search.matchhighlight.MatchHighlighter;
import org.apache.lucene.search.matchhighlight.PassageFormatter;
import org.apache.lucene.store.MMapDirectory;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;

import static org.apache.lucene.tests.util.LuceneTestCase.newStringField;

public class HighLightDemo {
    MMapDirectory mMapDirectory = null;
    IndexWriter indexWriter = null;
    private FieldType TYPE_TEXT_POSITIONS_OFFSETS;
    private FieldType TYPE_TEXT_POSITIONS;
    WhitespaceAnalyzer whitespaceAnalyzer =null;
    String uri = "F:\\integration\\search\\lucene-data\\highlight";

    @AfterEach
    public void tearDown() throws IOException {

        Files.walkFileTree(Paths.get(uri), new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
              throws IOException
            {
                Files.delete(file);
                return FileVisitResult.CONTINUE;
            }
         @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException e)
              throws IOException
            {
                if (e == null) {
                    Files.delete(dir);
                    return FileVisitResult.CONTINUE;
                } else {
                    // directory iteration failed
                    throw e;
                }
            }
        });
    }
    @BeforeEach
    public void setUp() {
        TYPE_TEXT_POSITIONS = TextField.TYPE_STORED;

        TYPE_TEXT_POSITIONS_OFFSETS = new FieldType(TextField.TYPE_STORED);
        TYPE_TEXT_POSITIONS_OFFSETS.setIndexOptions(
                IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS);
        TYPE_TEXT_POSITIONS_OFFSETS.freeze();

        try {
            mMapDirectory = new MMapDirectory(Paths.get(uri));
            IndexWriterConfig indexWriterConfig = new IndexWriterConfig();
            indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
            whitespaceAnalyzer = new WhitespaceAnalyzer();
            indexWriter = new IndexWriter(mMapDirectory, new IndexWriterConfig(whitespaceAnalyzer));



        } catch (IOException e) {
            throw new RuntimeException("mmap failed");
        }

    }

    private static final String FLD_ID = "id";
    private static final String FLD_TEXT1 = "text1";
    private static final String FLD_TEXT2 = "text2";

    private IndexableField toField(String name, String value) {
        switch (name) {
            case FLD_TEXT1:
                return new Field(name, value, TYPE_TEXT_POSITIONS_OFFSETS);
            case FLD_TEXT2:
                return new Field(name, value, TYPE_TEXT_POSITIONS);
            default:
                throw new AssertionError("Don't know how to handle this field: " + name);
        }
    }
    @Test
    public void walkFileTree() throws IOException {
        // could recursion walk the file tree
        String uri="F:\\integration\\search\\lucene-data";
        ArrayList<String> strings = Lists.<String>newArrayList();

        Files.walkFileTree(Paths.get(uri), new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                    throws IOException
            {
                strings.add(file.toString());
                return FileVisitResult.CONTINUE;
            }
        });
        System.out.println(strings);

    }
    @Test
    public void indexAndSearch() throws IOException, InvalidTokenOffsetsException, ParseException {
        String[] docs =
                new String[]{
                        "Liberty is a highly composable and dynamic runtime environment. ", // 0
                        "You can install the server as described in Installing Liberty by using downloaded archives.", // 1
                        "Because Liberty does not include a Java™ runtime environment (JRE), you must install a compliant Java implementation (JRE or SDK) beforehand. For more information about supported Java environments and where to get them, see Minimum supported Java levels.", // 2
                        "This server supports two models of application deployment:", // 3
                };
        for (int i = 0; i < docs.length; i++) {
            Document doc = new Document();
            // id:0 id:1 id:2  id:3
            doc.add(newStringField(FLD_ID, "" + i, Field.Store.YES));
            doc.add(toField(FLD_TEXT1, docs[i]));
            if(i==3){
                doc.add(toField(FLD_TEXT2, "no content for test"));
            }
            indexWriter.addDocument(doc);
        }


        DirectoryReader reader = DirectoryReader.open(indexWriter);
        IndexSearcher indexSearcher = new IndexSearcher(reader);
        BooleanQuery.Builder builder = new BooleanQuery.Builder();
        builder.add(new TermQuery(new Term(FLD_TEXT1,"Liberty")), BooleanClause.Occur.SHOULD);
        builder.add(new TermQuery(new Term(FLD_TEXT2,"test")), BooleanClause.Occur.SHOULD);
        BooleanQuery build = builder.build();


        Sort sortOrder = Sort.INDEXORDER;
        TopFieldDocs search = indexSearcher.search(build, 10, sortOrder);

        MatchHighlighter matchHighlighter = makeUpHighLightMethodThree(indexSearcher);

        List<List<String>> collect = matchHighlighter.highlight(search, build).map(docHighlights -> {
            // id: 1
            // text1: Liberty is a highly...
            return docHighlights.fields.entrySet().stream().map(e -> e.getKey() + ": " + String.join(", ", e.getValue()))
                    .collect(Collectors.toList());
        }).collect(Collectors.toList());

        assertHighlights(collect);


        reader.close();;
        indexWriter.close();;
    }
    /**
     [ 0. id: 0,     text1: >Liberty< is a highly composable and dynamic runtime environment. ,  1. id: 1,     text1: You can install the server as described in Installing >Liberty< by using downloaded archives.,  2. id: 2,     text1: Because >Liberty< does not include a Java™ runtime environment (JRE), you must install a compliant Java implementation (JRE or SDK) beforehand. For more information about supported Java environments and where to get them, see Minimum...,  3. id: 3,     text1: This...,     text2: no content for >test<]

     */
    private MatchHighlighter  makeUpHighLightMethodThree(IndexSearcher indexSearcher){
        Set<String> fields = Set.of(FLD_TEXT1, FLD_TEXT2);
        MatchHighlighter.FieldValueHighlighter highlightedOrAbbreviated =
                FieldValueHighlighters.highlighted(
                        80 * 3, 1, new PassageFormatter("...", ">", "<"), fields::contains)
                        .or(FieldValueHighlighters.maxLeadingCharacters(10, "...", fields));

        MatchHighlighter matchHighlighter = new MatchHighlighter(indexSearcher, whitespaceAnalyzer)
                .appendFieldHighlighter(FieldValueHighlighters.verbatimValue(FLD_ID))
                .appendFieldHighlighter(highlightedOrAbbreviated)
                .appendFieldHighlighter(FieldValueHighlighters.skipRemaining());
        return matchHighlighter;
    }

    private MatchHighlighter  makeUpHighLightMethodTwo(IndexSearcher indexSearcher){
        MatchHighlighter matchHighlighter = new MatchHighlighter(indexSearcher, whitespaceAnalyzer)
                .appendFieldHighlighter(FieldValueHighlighters.verbatimValue(FLD_ID))
                .appendFieldHighlighter(FieldValueHighlighters.highlighted(80 * 3, 1,
                        new PassageFormatter("...", ">", "<"), FLD_TEXT1::equals))
                .appendFieldHighlighter(FieldValueHighlighters.maxLeadingCharacters(10,"...", Set.of(FLD_TEXT1)))
                .appendFieldHighlighter(FieldValueHighlighters.skipRemaining());
        return matchHighlighter;
    }

    private MatchHighlighter   makeUpHighLightMethod(IndexSearcher indexSearcher){
        MatchHighlighter matchHighlighter = new MatchHighlighter(indexSearcher, whitespaceAnalyzer)
                .appendFieldHighlighter(FieldValueHighlighters.verbatimValue(FLD_ID))
                .appendFieldHighlighter(FieldValueHighlighters.highlighted(80 * 3, 1,
                        new PassageFormatter("...", ">", "<"), FLD_TEXT1::equals))
                .appendFieldHighlighter(FieldValueHighlighters.skipRemaining());
        return matchHighlighter;
    }

    private void assertHighlights(List<List<String>> docList) {
        ArrayList<String> actualLines = new ArrayList<>();
        for (int doc = 0; doc < docList.size(); doc++) {
            List<String> fields = docList.get(doc);
            for (int i = 0; i < fields.size(); i++) {
                actualLines.add(
                        (i == 0 ? String.format(Locale.ROOT, "%2d. ", doc) : "    ") + fields.get(i));
            }
        }
        System.out.println(actualLines.toString());
    }

    public void testBasicUsage() throws Exception {

    }

    public void verify() throws ParseException, IOException, InvalidTokenOffsetsException {
        Analyzer analyzer = new WhitespaceAnalyzer();

        DirectoryReader open = DirectoryReader.open(mMapDirectory);
        IndexSearcher searcher = new IndexSearcher(open);

        QueryParser parser = new QueryParser("notv", analyzer);
        Query query = parser.parse("million");

        TopDocs hits = searcher.search(query, 10);

        SimpleHTMLFormatter htmlFormatter = new SimpleHTMLFormatter();
        Highlighter highlighter = new org.apache.lucene.search.highlight.Highlighter(htmlFormatter, new QueryScorer(query));
        for (int i = 0; i < 10; i++) {
            int id = hits.scoreDocs[i].doc;
            Document doc = searcher.doc(id);
            String text = doc.get("notv");
            TokenStream tokenStream = TokenSources.getAnyTokenStream(searcher.getIndexReader(), id, "notv", analyzer);
            TextFragment[] frag = highlighter.getBestTextFragments(tokenStream, text, false, 10);//highlighter.getBestFragments(tokenStream, text, 3, "...");
            for (int j = 0; j < frag.length; j++) {
                if ((frag[j] != null) && (frag[j].getScore() > 0)) {
                    System.out.println((frag[j].toString()));
                }
            }
            //Term vector
            text = doc.get("tv");
            tokenStream = TokenSources.getAnyTokenStream(searcher.getIndexReader(), hits.scoreDocs[i].doc, "tv", analyzer);
            frag = highlighter.getBestTextFragments(tokenStream, text, false, 10);
            for (int j = 0; j < frag.length; j++) {
                if ((frag[j] != null) && (frag[j].getScore() > 0)) {
                    System.out.println((frag[j].toString()));
                }
            }
            System.out.println("-------------");
        }
    }
}